﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects;

using System.Data.Objects.DataClasses;

namespace JULONG.WEB.Library
{
    /// <summary>
    /// ef框架会话类
    /// </summary>
    /// <typeparam name="D"></typeparam>
    /// <typeparam name="T"></typeparam>
    public class DataSession<D, T>
        where D : System.Data.Objects.ObjectContext, new()
        where T : System.Data.Objects.DataClasses.EntityObject
    {

        //ObjectContext _db;

        /// <summary>
        /// 设置或返回会话的基本ObjectContext
        /// </summary>
        //public ObjectContext db { get { return _db; } set { _db = value; } }

        public ObjectContext db = new D();

        ObjectQuery<T> _query;

        public DataSession(ObjectContext objectContext = null)
        {
            this.db = objectContext ?? new D();
            //_db = new D();
            _query = db.CreateQuery<T>(string.Format("[{0}]", typeof(T).Name));//防止关键字
        }

        ///// <summary>
        ///// 连接字符串(使用反射，建议弃用，应在 ObjectContext 子类中实现)
        ///// </summary>
        ///// <param name="connStr"></param>
        //public DataSession(string connStr)
        //{
        //    Type d = typeof(D);
        //    Type[] tp = new Type[1];
        //    tp[0] = typeof(string);
        //    System.Reflection.ConstructorInfo ci = d.GetConstructor(tp);
        //    object[] op = new object[1];
        //    op[0] = connStr;
        //    this.db = (D)ci.Invoke(op);
        //}

        /// <summary>
        /// 根据现有会话建立一个新会话，新会话的ObjectContext保持不变
        /// </summary>
        /// <typeparam name="newT"></typeparam>
        /// <returns></returns>
        public DataSession<D, newT> newSession<newT>()
            where newT : System.Data.Objects.DataClasses.EntityObject
        {
            var newSession = new DataSession<D, newT>(this.db);
            //newSession.db = this.db;
            return newSession;
        }


        /// <summary>
        /// 创建一个空实体
        /// </summary>
        /// <returns></returns>
        public T create() 
        {
            return db.CreateObject<T>();
            //return new T();
        }

        /// <summary>
        /// 返回全部结果集
        /// </summary>
        /// <returns></returns>
        public IQueryable<T> query() //写成并行AsParallel不好使
        {
            return _query;
            //return db.CreateQuery<T>(typeof(T).Name);
        }

        /// <summary>
        /// 中间采用了(IEnumerable) Where 转为IEnumerable
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        [System.Obsolete("此方法会调用(IEnumerable) Where 将结果转化为IEnumerable，非常耗时，建议用query().where(lambda)")]
        public IQueryable<T> query(Func<T, bool> lambda) //写成并行AsParallel不好使
        {
            return query().Where(lambda).AsQueryable();
            // return Queryable.Where<T>(query(), lambda);
        }

        /// <summary>
        /// 根据int类型的ID主键返回一条记录（注意ID是大写）
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public T getByID(object id)
        {
            return getByPK(id, "ID");
        }

        /// <summary>
        /// 根据主键返回一条实体记录（未实现联合主键）
        /// xxx.getByPK(5,"ID")
        /// </summary>
        /// <param name="value"></param>
        /// <param name="PKName"></param>
        /// <returns></returns>
        public T getByPK(object value, string PKName)
        {
            //取得主键
            //var key = db.CreateEntityKey(typeof(T).FullName, create());
            System.Data.EntityKey key = new System.Data.EntityKey(typeof(T).FullName, PKName, value);
            object t = null;
            db.TryGetObjectByKey(key, out t);
            //var cst1 = db.GetObjectByKey(key) as T;
            return t as T;
        }

        /// <summary>
        /// 返回唯一一条实体记录
        /// 如未查询到则返回空，如多个则引发异常
        /// </summary>
        /// <param name="lambda">查询表达式</param>
        /// <returns></returns>
        public T get(Func<T, Boolean> lambda)
        {
            return query().SingleOrDefault(lambda);
        }


        /// <summary>
        /// 返回唯一条实体记录
        /// 如多个则返回第一个，不存在则新创建一个
        /// </summary>
        /// <param name="lambda">查询表达式</param>
        /// <returns></returns>
        public T tryGet(Func<T, Boolean> lambda)
        {
            return query().FirstOrDefault(lambda) ?? create();
            //if (query().Where(lambda).Count() != 1) return create();
            //return get(lambda);
        }

        /// <summary>
        /// 得到实体的原始值(数据库值)
        /// </summary>
        /// <param name="lambda">查询表达式</param>
        /// <returns></returns>
        public T getOriginalValue(Func<T, Boolean> lambda)
        {
            var newDataSession = new DataSession<D, T>();
            return newDataSession.get(lambda);
            //ef4.1实现的将更加优雅：
            //return ObjectContext.Entry(T).OriginalValues;
        }

        /// <summary>
        /// 插入一条实体记录
        /// </summary>
        /// <param name="t">实体记录</param>
        /// <param name="save">是否立即存储，默认为真</param>
        /// <returns></returns>
        public T insert(T t, bool save = true)
        {
            db.AddObject(typeof(T).Name, t);
            if (save) db.SaveChanges();
            return t;
        }

        /// <summary>
        /// 插入一组记录
        /// </summary>
        /// <param name="t">实体记录组</param>
        /// <param name="save">是否立即存储，默认为真</param>
        /// <returns></returns>
        public IEnumerable<T> insert(IEnumerable<T> t, bool save = true)
        {
            foreach (var item in t)
            {
                db.AddObject(typeof(T).Name, item);
            }
            if (save) db.SaveChanges();
            return t;
        }

        /// <summary>
        /// 删除一条实体记录
        /// </summary>
        /// <param name="t">实体记录</param>
        /// <param name="save">是否立即存储，默认为真</param>
        public void delete(T t, bool save = true)
        {
            db.DeleteObject(t);
            if (save) db.SaveChanges();
        }

        /// <summary>
        /// 删除满足条件的记录
        /// 采用的是IQueryable删除，支持Expression<Func<TSource, int, bool>，因此lambda可以使用委托，进行复杂运算
        /// 如：(删除时返回被删元素的某个字端值
        ///    int aa = 0;
        ///     
        ///    db.delete((d) => {
        ///         
        ///    aa = d.channelID;
        ///
        ///    return d.ID == docId; });
        /// </summary>
        /// <param name="lambda">查询表达式</param>
        /// <param name="save">是否立即存储，默认为真</param>
        public void delete(Func<T, Boolean> lambda, bool save = true)
        {
            foreach (var item in query().Where(lambda))
            {
                db.DeleteObject(item);
            }
            if (save) db.SaveChanges();
        }

        /// <summary>
        /// 更新记录实体记录
        /// </summary>
        /// <param name="t">更新后的实体记录</param>
        /// <param name="save">是否立即存储，默认为真</param>
        public void update(T t, bool save = true)
        {
            db.AttachTo(typeof(T).Name, t);
            db.ObjectStateManager.ChangeObjectState(t, System.Data.EntityState.Modified);
            if (save) db.SaveChanges();
        }

        /// <summary>
        /// 保存提交的更改
        /// </summary>
        public void save()
        {
            db.SaveChanges();
        }

        /// <summary>
        /// 返回当前上下文生成的SQL语句
        /// </summary>
        /// <returns></returns>
        public string getSql()
        {
            return _query.ToTraceString();
        }

        /// <summary>
        /// 释放对象上下文使用的资源。
        /// </summary>
        public void Dispose()
        {
            db.Dispose();
        }
    }
}